/*
 * Copyright (C) 2010 TopCoder Inc., All Rights Reserved.
 */
package com.hercules.xre.protocol.testing.runner;

import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.hercules.xre.protocol.testing.EventHandler;
import com.hercules.xre.protocol.testing.ProtocolTestingConnectionManagerException;
import com.hercules.xre.protocol.testing.handlers.CommandSendingEventHandler;
import com.hercules.xre.protocol.testing.handlers.EventType;
import com.hercules.xre.protocol.testing.handlers.TestCaseEventHandler;
import com.hercules.xre.protocol.testing.handlers.commandretriever.FileCommandRetriever;
import com.hercules.xre.protocol.testing.netty.BaseProtocolTestingConnectionManager;
import com.topcoder.util.log.Level;
import com.topcoder.util.log.Log;
import com.topcoder.util.log.LogManager;

/**
 * <p>
 * Test server for protocol testing.
 * </p>
 * <p>
 * Thread Safety: it's mutable and not thread safe.
 * </p>
 *
 * @author TCSASSEMBLER
 * @version 1.0
 */
public class TestServer {

    /**
     * Logger for the server.
     */
    private static final Log LOGGER = LogManager.getLog(TestServer.class.getName());

    /**
     * {@link BaseProtocolTestingConnectionManager} instance.
     */
    private BaseProtocolTestingConnectionManager connectionManager;

    /**
     * {@link FileCommandRetriever} instance.
     */
    private FileCommandRetriever commandRetriever;

    /**
     * Default constructor.
     *
     * @param connectionManager The connection manager.
     * @param runner the test runner.
     * @throws ProtocolTestingConnectionManagerException if any error occurs
     */
    public TestServer(BaseProtocolTestingConnectionManager connectionManager, TestRunnerGUI runner)
        throws ProtocolTestingConnectionManagerException {
        this.connectionManager = connectionManager;

        commandRetriever = new FileCommandRetriever();
        commandRetriever.setFilename(Constants.START_FILE);

        CommandSendingEventHandler commandSendingEventHandler = new CommandSendingEventHandler();
        commandSendingEventHandler.setCommandRetriever(commandRetriever);
        commandSendingEventHandler.setProtocolTestingConnectionManager(connectionManager);

        TestCaseEventHandler testCaseEventHandler = new TestCaseEventHandler();
        Map<EventType, List<EventHandler>> handlers = new HashMap<EventType, List<EventHandler>>();
        handlers.put(EventType.CONNECTION_DISCONNECTED, Arrays.asList((EventHandler) commandSendingEventHandler));
        handlers.put(EventType.CONNECTION_SHUTDOWN, Arrays.asList((EventHandler) commandSendingEventHandler));
        handlers.put(EventType.CONNECTION_CREATED, Arrays.asList((EventHandler) commandSendingEventHandler));
        handlers.put(EventType.CONNECTION_EVENT, Arrays.asList((EventHandler) commandSendingEventHandler));

        new File(Constants.LOG_FILE).delete();
        testCaseEventHandler.setEventHandlers(handlers);
        testCaseEventHandler.setReportFileName(Constants.LOG_FILE);
        testCaseEventHandler.setRunner(runner);
        testCaseEventHandler.setAppendToReportFile(true);

        runner.setTestCaseEventHandler(testCaseEventHandler);
        connectionManager.startAcceptingConnections(testCaseEventHandler);
        LOGGER.log(Level.DEBUG, "Server has successfully started.");
    }

    /**
     * Update the command file.
     *
     * @param commandFile the command file.
     */
    public void updateCommandsFile(String commandFile) {
        commandRetriever.setFilename(commandFile);
    }

    /**
     * Restart the client.
     *
     * @throws ProtocolTestingConnectionManagerException if any error occurs while sending the restart command.
     */
    public void restartClient() throws ProtocolTestingConnectionManagerException {
        String command = "{\"commandIndex\":0,\"timestamp\":0,\"reason\":null,"
                + "\"statusCode\":0,\"command\":\"RESTART\"}";
        connectionManager.sendCommand(Constants.SESSION_GUID, connectionManager.prepareCommandToSend(command,
                connectionManager.getChannelBySessionGUID(Constants.SESSION_GUID)));
        LOGGER.log(Level.DEBUG, "Send restart command to the client");
    }

    /**
     * Shutdown the client.
     *
     * @throws ProtocolTestingConnectionManagerException if any error occurs while sending the shutdown command.
     */
    public void shutdownClient() throws ProtocolTestingConnectionManagerException {
        String command = "{\"commandIndex\":0,\"timestamp\":0,\"command\":\"SHUTDOWN\"}";
        connectionManager.sendCommand(Constants.SESSION_GUID, connectionManager.prepareCommandToSend(command,
                connectionManager.getChannelBySessionGUID(Constants.SESSION_GUID)));
        LOGGER.log(Level.DEBUG, "Send shutdown command to the client");
    }

    /**
     * Dispose the connection manager.
     *
     * @throws ProtocolTestingConnectionManagerException if any error occurs while disposing the server.
     */
    public void dispose() throws ProtocolTestingConnectionManagerException {
        connectionManager.dispose();
        LOGGER.log(Level.DEBUG, "Server has successfully disposed.");
    }
}
